1   /*
2    * Copyright (C) 2007 The Guava Authors
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package com.google.common.collect.testing.testers;
18  
19  import static com.google.common.collect.testing.features.CollectionSize.ZERO;
20  import static com.google.common.collect.testing.features.MapFeature.ALLOWS_NULL_KEYS;
21  import static com.google.common.collect.testing.features.MapFeature.ALLOWS_NULL_VALUES;
22  import static com.google.common.collect.testing.features.MapFeature.FAILS_FAST_ON_CONCURRENT_MODIFICATION;
23  import static com.google.common.collect.testing.features.MapFeature.SUPPORTS_PUT;
24  
25  import com.google.common.annotations.GwtCompatible;
26  import com.google.common.collect.testing.AbstractMapTester;
27  import com.google.common.collect.testing.features.CollectionSize;
28  import com.google.common.collect.testing.features.MapFeature;
29  
30  import java.util.ConcurrentModificationException;
31  import java.util.Iterator;
32  import java.util.Map;
33  import java.util.Map.Entry;
34  
35  /**
36   * A generic JUnit test which tests {@code put} operations on a map. Can't be
37   * invoked directly; please see
38   * {@link com.google.common.collect.testing.MapTestSuiteBuilder}.
39   *
40   * @author Chris Povirk
41   * @author Kevin Bourrillion
42   */
43  @SuppressWarnings("unchecked") // too many "unchecked generic array creations"
44  @GwtCompatible(emulated = true)
45  public class MapPutTester<K, V> extends AbstractMapTester<K, V> {
46    private Entry<K, V> nullKeyEntry;
47    private Entry<K, V> nullValueEntry;
48    private Entry<K, V> nullKeyValueEntry;
49    private Entry<K, V> presentKeyNullValueEntry;
50  
51    @Override public void setUp() throws Exception {
52      super.setUp();
53      nullKeyEntry = entry(null, samples.e3.getValue());
54      nullValueEntry = entry(samples.e3.getKey(), null);
55      nullKeyValueEntry = entry(null, null);
56      presentKeyNullValueEntry = entry(samples.e0.getKey(), null);
57    }
58  
59    @MapFeature.Require(SUPPORTS_PUT)
60    public void testPut_supportedNotPresent() {
61      assertNull("put(notPresent, value) should return null", put(samples.e3));
62      expectAdded(samples.e3);
63    }
64  
65    @MapFeature.Require({FAILS_FAST_ON_CONCURRENT_MODIFICATION, SUPPORTS_PUT})
66    @CollectionSize.Require(absent = ZERO)
67    public void testPutAbsentConcurrentWithEntrySetIteration() {
68      try {
69        Iterator<Entry<K, V>> iterator = getMap().entrySet().iterator();
70        put(samples.e3);
71        iterator.next();
72        fail("Expected ConcurrentModificationException");
73      } catch (ConcurrentModificationException expected) {
74        // success
75      }
76    }
77  
78    @MapFeature.Require({FAILS_FAST_ON_CONCURRENT_MODIFICATION, SUPPORTS_PUT})
79    @CollectionSize.Require(absent = ZERO)
80    public void testPutAbsentConcurrentWithKeySetIteration() {
81      try {
82        Iterator<K> iterator = getMap().keySet().iterator();
83        put(samples.e3);
84        iterator.next();
85        fail("Expected ConcurrentModificationException");
86      } catch (ConcurrentModificationException expected) {
87        // success
88      }
89    }
90  
91    @MapFeature.Require({FAILS_FAST_ON_CONCURRENT_MODIFICATION, SUPPORTS_PUT})
92    @CollectionSize.Require(absent = ZERO)
93    public void testPutAbsentConcurrentWithValueIteration() {
94      try {
95        Iterator<V> iterator = getMap().values().iterator();
96        put(samples.e3);
97        iterator.next();
98        fail("Expected ConcurrentModificationException");
99      } catch (ConcurrentModificationException expected) {
100       // success
101     }
102   }
103 
104   @MapFeature.Require(absent = SUPPORTS_PUT)
105   public void testPut_unsupportedNotPresent() {
106     try {
107       put(samples.e3);
108       fail("put(notPresent, value) should throw");
109     } catch (UnsupportedOperationException expected) {
110     }
111     expectUnchanged();
112     expectMissing(samples.e3);
113   }
114 
115   @MapFeature.Require(absent = SUPPORTS_PUT)
116   @CollectionSize.Require(absent = ZERO)
117   public void testPut_unsupportedPresentExistingValue() {
118     try {
119       assertEquals("put(present, existingValue) should return present or throw",
120           samples.e0.getValue(), put(samples.e0));
121     } catch (UnsupportedOperationException tolerated) {
122     }
123     expectUnchanged();
124   }
125 
126   @MapFeature.Require(absent = SUPPORTS_PUT)
127   @CollectionSize.Require(absent = ZERO)
128   public void testPut_unsupportedPresentDifferentValue() {
129     try {
130       getMap().put(samples.e0.getKey(), samples.e3.getValue());
131       fail("put(present, differentValue) should throw");
132     } catch (UnsupportedOperationException expected) {
133     }
134     expectUnchanged();
135   }
136 
137   @MapFeature.Require({SUPPORTS_PUT, ALLOWS_NULL_KEYS})
138   public void testPut_nullKeySupportedNotPresent() {
139     assertNull("put(null, value) should return null", put(nullKeyEntry));
140     expectAdded(nullKeyEntry);
141   }
142 
143   @MapFeature.Require({SUPPORTS_PUT, ALLOWS_NULL_KEYS})
144   @CollectionSize.Require(absent = ZERO)
145   public void testPut_nullKeySupportedPresent() {
146     Entry<K, V> newEntry = entry(null, samples.e3.getValue());
147     initMapWithNullKey();
148     assertEquals("put(present, value) should return the associated value",
149         getValueForNullKey(), put(newEntry));
150 
151     Entry<K, V>[] expected = createArrayWithNullKey();
152     expected[getNullLocation()] = newEntry;
153     expectContents(expected);
154   }
155 
156   @MapFeature.Require(value = SUPPORTS_PUT, absent = ALLOWS_NULL_KEYS)
157   public void testPut_nullKeyUnsupported() {
158     try {
159       put(nullKeyEntry);
160       fail("put(null, value) should throw");
161     } catch (NullPointerException expected) {
162     }
163     expectUnchanged();
164     expectNullKeyMissingWhenNullKeysUnsupported(
165         "Should not contain null key after unsupported put(null, value)");
166   }
167 
168   @MapFeature.Require({SUPPORTS_PUT, ALLOWS_NULL_VALUES})
169   public void testPut_nullValueSupported() {
170     assertNull("put(key, null) should return null", put(nullValueEntry));
171     expectAdded(nullValueEntry);
172   }
173 
174   @MapFeature.Require(value = SUPPORTS_PUT, absent = ALLOWS_NULL_VALUES)
175   public void testPut_nullValueUnsupported() {
176     try {
177       put(nullValueEntry);
178       fail("put(key, null) should throw");
179     } catch (NullPointerException expected) {
180     }
181     expectUnchanged();
182     expectNullValueMissingWhenNullValuesUnsupported(
183         "Should not contain null value after unsupported put(key, null)");
184   }
185 
186   @MapFeature.Require({SUPPORTS_PUT, ALLOWS_NULL_VALUES})
187   @CollectionSize.Require(absent = ZERO)
188   public void testPut_replaceWithNullValueSupported() {
189     assertEquals("put(present, null) should return the associated value",
190         samples.e0.getValue(), put(presentKeyNullValueEntry));
191     expectReplacement(presentKeyNullValueEntry);
192   }
193 
194   @MapFeature.Require(value = SUPPORTS_PUT, absent = ALLOWS_NULL_VALUES)
195   @CollectionSize.Require(absent = ZERO)
196   public void testPut_replaceWithNullValueUnsupported() {
197     try {
198       put(presentKeyNullValueEntry);
199       fail("put(present, null) should throw");
200     } catch (NullPointerException expected) {
201     }
202     expectUnchanged();
203     expectNullValueMissingWhenNullValuesUnsupported(
204         "Should not contain null after unsupported put(present, null)");
205   }
206 
207   @MapFeature.Require({SUPPORTS_PUT, ALLOWS_NULL_VALUES})
208   @CollectionSize.Require(absent = ZERO)
209   public void testPut_replaceNullValueWithNullSupported() {
210     initMapWithNullValue();
211     assertNull("put(present, null) should return the associated value (null)",
212         getMap().put(getKeyForNullValue(), null));
213     expectContents(createArrayWithNullValue());
214   }
215 
216   @MapFeature.Require({SUPPORTS_PUT, ALLOWS_NULL_VALUES})
217   @CollectionSize.Require(absent = ZERO)
218   public void testPut_replaceNullValueWithNonNullSupported() {
219     Entry<K, V> newEntry = entry(getKeyForNullValue(), samples.e3.getValue());
220     initMapWithNullValue();
221     assertNull("put(present, value) should return the associated value (null)",
222         put(newEntry));
223 
224     Entry<K, V>[] expected = createArrayWithNullValue();
225     expected[getNullLocation()] = newEntry;
226     expectContents(expected);
227   }
228 
229   @MapFeature.Require({SUPPORTS_PUT, ALLOWS_NULL_KEYS, ALLOWS_NULL_VALUES})
230   public void testPut_nullKeyAndValueSupported() {
231     assertNull("put(null, null) should return null", put(nullKeyValueEntry));
232     expectAdded(nullKeyValueEntry);
233   }
234 
235   private V put(Map.Entry<K, V> entry) {
236     return getMap().put(entry.getKey(), entry.getValue());
237   }
238 }
239